#include "model.h"
#include "c_defines.h"
#include "model/mesh.h"
#include "shader.h"
#include <assimp/Importer.hpp>
#include <assimp/mesh.h>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include <type_traits>

using namespace Assimp;
using namespace std;

CModel::~CModel() {}

bool CModel::LoadModel(const std::string &path)
{
    Importer importer;

    /**
     * @brief 通过assimp库加载模型
     * @param pFlags 指定后处理操作
     * @ref aiProcess_CalcTangentSpace 计算导入网格的法线和切线向量
     * @ref aiProcess_Triangulate 测量所有网格的所有面,将三个顶点以上的面转化为三角形
     * @ref aiProcess_JoinIdenticalVertices 所有网格的面将共用重复的顶点
     * @ref aiProcess_SortByPType 合并同类型的基本类型网格,通常用于去除很少使用的点和线
     */
    const aiScene *scene = importer.ReadFile(path, aiProcess_CalcTangentSpace | aiProcess_Triangulate |
                                                       aiProcess_JoinIdenticalVertices | aiProcess_SortByPType);
    if ((!scene) || (scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE) || (!scene->mRootNode))
    {
        ERROR("Assimp:: %s\n", importer.GetErrorString());
        return false;
    }

    ProcessNode(scene->mRootNode, scene);

    for (auto &item : m_meshes)
        item.Init();

    return true;
}

/**
 * @brief 递归的方式处理节点
 * @details node中只保存了索引,用来引用scene中的实际对象
 */
void CModel::ProcessNode(aiNode *node, const aiScene *scene)
{
    for (unsigned i = 0; i < node->mNumMeshes; ++i)
    {
        aiMesh *mesh = scene->mMeshes[node->mMeshes[i]];
        m_meshes.emplace_back(ProcessMesh(mesh, scene));
    }

    for (unsigned i = 0; i < node->mNumChildren; ++i)
    {
        ProcessNode(node->mChildren[i], scene);
    }
}

/**
 * @brief 处理网格数据
 */
CMesh CModel::ProcessMesh(aiMesh *mesh, const aiScene *scene)
{
    vector<CMesh::SVertex>  vertices;
    vector<GLuint>          indices;
    vector<CMesh::STexture> textures;

    // 访问顶点属性
    for (unsigned i = 0; i < mesh->mNumVertices; ++i)
    {
        CMesh::SVertex vertex;
        vertex.position.x = mesh->mVertices[i].x;
        vertex.position.y = mesh->mVertices[i].y;
        vertex.position.z = mesh->mVertices[i].z;

        vertices.emplace_back(vertex);
    }

    // 遍历每一个网格面,并检索相应的顶点索引
    for (unsigned i = 0; i < mesh->mNumFaces; ++i)
    {
        auto face = mesh->mFaces[i];
        for (unsigned j = 0; j < face.mNumIndices; ++j)
        {
            indices.emplace_back(face.mIndices[j]);
        }
    }

    return CMesh(vertices, indices);
}

void CModel::Draw()
{
    for (auto &item : m_meshes)
    {
        item.DrawOnlyUseVertices();
    }
}